package handledb;

import java.sql.SQLException;
import java.util.*;
import java.util.logging.Logger;

public class TransactionManage {
    public static final Logger log = Logger.getLogger(TransactionManage.class.getSimpleName());
    public static final String COMMIT = "commit";
    public static final String ROLLBACK = "rollback";
    public static final String SHUTDOWN = "shutdown";
    volatile static Vector<TransactionItem> GLOBAL_TRANSACTION = new Vector<>();
    public static CloseHandle closeHandle = null;

    private static final Timer timer = new Timer();
    volatile static DbPool[] POOLS = new DbPool[5];
    private static volatile int poolSize = 0;
    static void putDbPool(DbPool dbPool){
        if(poolSize<1){
            TimerTask task = new TimerTask() {
                @Override
                public void run() {
                    Long time = System.currentTimeMillis();
                    for(int i=0;i<poolSize;i++){
                        POOLS[i].cleanTask(time);
                    }
                    clearTransaction();
                }
            };
            timer.schedule(task, 60000, 60000);
        }
        if(poolSize==POOLS.length){
            DbPool[] arrs = new DbPool[POOLS.length+5];
            for(int i=0;i<poolSize;i++){
                arrs[i] = POOLS[i];
            }
            POOLS = arrs;
        }
        POOLS[poolSize] = dbPool;
        poolSize+=1;
    }
    static {
        Runtime.getRuntime().addShutdownHook(new Thread(() -> {
            for(int i=0;i<poolSize;i++){
                POOLS[i].closeConnections();
            }
            if(closeHandle!=null){
                closeHandle.close(SHUTDOWN);
            }
        }));
    }

    public static TransactionItem getTransactionItem(Long threadId, Long transactionId){
        if (threadId!=null){
            for (TransactionItem transactionItem : GLOBAL_TRANSACTION) {
                if(threadId.equals(transactionItem.threadId)){
                    return transactionItem;
                }
            }
        } else if (transactionId!=null && !transactionId.equals(0)) {
            for (TransactionItem transactionItem : GLOBAL_TRANSACTION) {
                if(transactionId.equals(transactionItem.transactionId)){
                    return transactionItem;
                }
            }
        }
        return null;
    }
    private static TransactionItem addTransactionItem(Long effectiveTime, Long transactionId, Long threadId){
        if(threadId==null){
            threadId = Thread.currentThread().getId();
        }
        TransactionItem item = new TransactionItem(threadId,transactionId);
        GLOBAL_TRANSACTION.add(item);
        if(effectiveTime==null || effectiveTime.equals(0)){
            effectiveTime = POOLS[0].config.transactionTimeout;
        }
        item.effectiveTime = effectiveTime;
        return item;
    }
    public static TransactionItem putTransactionItem(Long effectiveTime, Long transactionId, Long threadId){
        for (TransactionItem transactionItem : GLOBAL_TRANSACTION) {
            if(transactionId.equals(transactionItem.transactionId)){
                return transactionItem;
            }
        }
        return addTransactionItem(effectiveTime,transactionId,threadId);
    }

    public static void addUrl(String url, Long threadId, Long transactionId){
        if(url!=null && !url.equals("")){
            TransactionItem item = getTransactionItem(threadId,transactionId);
            if(item==null){
                item = addTransactionItem(null,transactionId,threadId);
            }
            item.putUrl(url);
        }
    }

    public static void commitAndCloseAll(Long transactionId, Long threadId){
        if(closeHandle==null){
            for(int i=0;i<poolSize;i++){
                POOLS[i].commitAndCloseCurrentPipeline();
            }
        }else{
            TransactionItem item = getTransactionItem(threadId,transactionId);
            if(item!=null && item.isLocal){
                closeHandle.dealTransaction(COMMIT,item);
                GLOBAL_TRANSACTION.remove(item);
            }
            for(int i=0;i<poolSize;i++){
                POOLS[i].commitAndCloseCurrentPipeline();
            }
            closeHandle.close(COMMIT);
        }
    }

    public static void dealGlobalTransaction(String status, Long transactionId) throws SQLException {
        TransactionItem item = null;
        if(closeHandle!=null){
            if(transactionId!=null){
                for (TransactionItem transactionItem : GLOBAL_TRANSACTION) {
                    if(transactionId.equals(transactionItem.transactionId)){
                        item = transactionItem;
                        break;
                    }
                }
            } else {
                long threadId = Thread.currentThread().getId();
                for (TransactionItem transactionItem : GLOBAL_TRANSACTION) {
                    if(transactionItem.threadId==threadId){
                        item = transactionItem;
                        break;
                    }
                }
            }
            if(item!=null){
                closeHandle.dealTransaction(status,item);
            }
        }
        if(COMMIT.equals(status)){
            for(int i=0;i<poolSize;i++){
                POOLS[i].getPipeline().commit();
            }
        } else if (ROLLBACK.equals(status)) {
            for(int i=0;i<poolSize;i++){
                POOLS[i].getPipeline().rollback();
            }
        }
    }

    public static void clearTransaction(){
        long t = System.currentTimeMillis();
        int size = GLOBAL_TRANSACTION.size();
        TransactionItem item;
        for (int i = 0; i < size; i++) {
            item = GLOBAL_TRANSACTION.get(i);
            if((t-item.time)>item.effectiveTime){
                log.info("auto clear transaction id: "+item.transactionId);
                for(int k=0;k<poolSize;k++){
                    POOLS[k].closeTransaction(item.transactionId, ROLLBACK);
                }
                GLOBAL_TRANSACTION.remove(i);
                size--;
                i--;
            }
        }
    }

    public static void closeTransaction(Long transactionId, String status){
        for(int i=0;i<poolSize;i++){
            POOLS[i].closeTransaction(transactionId, status);
        }
        TransactionItem transactionItem = null;
        for (TransactionItem item : GLOBAL_TRANSACTION) {
            if(transactionId.equals(item.transactionId)){
                transactionItem = item;
                break;
            }
        }
        GLOBAL_TRANSACTION.remove(transactionItem);
        if(closeHandle!=null){
            closeHandle.dealTransaction(status, transactionItem);
            closeHandle.close(status);
        }
    }

}
